home *** CD-ROM | disk | FTP | other *** search
/ Night Owl 6 / Night Owl's Shareware - PDSI-006 - Night Owl Corp (1990).iso / 019a / tde10src.zip / ED.C < prev    next >
C/C++ Source or Header  |  1991-06-05  |  44KB  |  1,420 lines

  1. /*******************  start of original comments  ********************/
  2. /*
  3.  * Written by Douglas Thomson (1989/1990)
  4.  *
  5.  * This source code is released into the public domain.
  6.  */
  7.  
  8. /*
  9.  * Name:    dte - Doug's Text Editor program - main editor module
  10.  * Purpose: This file contains the main editor module, and a number of the
  11.  *           smaller miscellaneous editing commands.
  12.  *          It also contains the code for dispatching commands.
  13.  * File:    ed.c
  14.  * Author:  Douglas Thomson
  15.  * System:  this file is intended to be system-independent
  16.  * Date:    October 1, 1989
  17.  * I/O:     file being edited
  18.  *          files read or written
  19.  *          user commands and prompts
  20.  * Notes:   see the file "dte.doc" for general program documentation
  21.  */
  22. /*********************  end of original comments   ********************/
  23.  
  24. /*
  25.  * The basic editor routines have been EXTENSIVELY reworked.  I have added
  26.  * support for lines longer than 80 columns and I have added line number
  27.  * support.  I like to know the real line number that editor functions are
  28.  * working on and I like to know the total number of lines in a file.
  29.  *
  30.  * I rewrote the big series of ifs in the dispatch subroutine.  It is now
  31.  * a series of case statements.  By doing so, it should be easier to implement a
  32.  * configuration file later on.
  33.  *
  34.  * I added a few functions that I use quite often and I deleted a few that I
  35.  * rarely use.  Added are Split Line, Join Line, and Duplicate Line.   Deleted
  36.  * are Word Delete, Goto Marker 0-9 (others?).
  37.  *
  38.  * I felt that the insert routine should be separated into two routines.  One
  39.  * for inserting the various combinations of newlines and one for inserting
  40.  * 'regular' text characters.
  41.  *
  42.  * One of Doug's design considerations was keeping screen updates to a minimum.
  43.  * I have expanded upon that idea and added support for updating windows
  44.  * LOCALly, GLOBALly, or NOT_LOCALly.  For example, scrolling in one window
  45.  * does not affect the text in another window - LOCAL update.  Adding, deleting,
  46.  * or modifing text in one window may affect text in other windows - GLOBAL
  47.  * update.  Sometimes, updates to the current window are handled in the task
  48.  * routines so updates to other windows are done NOT_LOCALly.
  49.  *
  50.  * Also note that using functions copy_line and un_copy_line to change a line
  51.  * automatically adjusts the g_status.end_mem pointer.  If a function bypasses
  52.  * those functions, adjusting the g_status.end_mem pointer must be done
  53.  * explicity.
  54.  *
  55.  * New editor name:  tde, the Thomson-Davis Editor.
  56.  * Author:           Frank Davis
  57.  * Date:             June 5, 1991
  58.  *
  59.  * This modification of Douglas Thomson's code is released into the
  60.  * public domain, Frank Davis.   You may distribute it freely.
  61.  */
  62.  
  63. #include "tdestr.h"     /* global variables */
  64. #include "global.h"     /* global variables */
  65. #include "define.h"
  66. #include "funcdef.h"
  67. #include "tdefunc.h"
  68.  
  69. /*
  70.  * Name:    tab_key
  71.  * Purpose: To make the necessary changes after the user types the tab key.
  72.  * Date:    June 5, 1991
  73.  * Passed:  window: information allowing access to the current window
  74.  * Notes:   If in insert mode, then this function adds the required
  75.  *           number of spaces in the file.
  76.  *          If not in insert mode, then tab simply moves the cursor right
  77.  *           the required distance.
  78.  */
  79. void tab_key( window )
  80. windows *window;
  81. {
  82. int spaces;  /* the spaces to move to the next tab stop */
  83. text_ptr source;    /* source for block move to make room for c */
  84. text_ptr dest;      /* destination for block move */
  85. long number;        /* number of characters to be moved */
  86. int space, pad, add, len, i, rcol, ccol;
  87. file_infos *file;
  88. int prompt_line;
  89.  
  90.    prompt_line = window->bottom_line;
  91.    rcol = window->rcol;
  92.    ccol = window->ccol;
  93.    /*
  94.     * work out the number of spaces to the next tab stop
  95.     */
  96.    spaces = g_status.tab_size - (rcol % g_status.tab_size);
  97.  
  98.    if (g_status.insert && rcol + spaces < g_display.line_length) {
  99.       copy_line( window->cursor, prompt_line );
  100.       /*
  101.        * work out how many characters need to be inserted
  102.        */
  103.       len = linelen( g_status.line_buff );
  104.       if (rcol > len)   /* padding required */
  105.          pad = rcol - len;
  106.       else
  107.          pad = 0;
  108.       if (g_status.line_buff[len] == CONTROL_Z)
  109.          ++pad;
  110.       if (len + pad + spaces >= g_display.line_length)
  111.          error( WARNING, window->bottom_line, "line too long to add" );
  112.       else {
  113.          file = window->file_info;
  114.          space = 0;
  115.          if (pad > 0  || spaces > 0) {
  116.             space = pad + spaces;
  117.             if (g_status.line_buff[len] == CONTROL_Z) {
  118.                g_status.line_buff[len] = '\n';
  119.                g_status.line_buff[len+1] = CONTROL_Z;
  120.                ++file->length;
  121.                show_size( window );
  122.                --pad;
  123.                ++len;
  124.             }
  125.             source = g_status.line_buff + rcol - pad;
  126.             dest = source + pad + spaces;
  127.             number = len + pad - rcol + 2;
  128.             hw_move( dest, source, number );
  129.  
  130.             /*
  131.              * if padding was required, then put in the required spaces
  132.              */
  133.             for (i=pad; i>0; i--)
  134.                *source++ = ' ';
  135.             for (i=spaces; i>0; i--)
  136.                *source++ = ' ';
  137.          }
  138.          un_copy_line( window->cursor, prompt_line );
  139.  
  140.          file->modified = TRUE;
  141.          adjust_start_end( file, space );
  142.          adjust_windows_cursor( window, (long)space, 0 );
  143.          window->dirty = GLOBAL;
  144.          show_changed_line( window );
  145.          show_avail_mem( );
  146.          rcol += spaces;
  147.          ccol += spaces;
  148.       }
  149.    } else if (rcol + spaces <= g_display.line_length) {
  150.       /*
  151.        * advance the cursor without changing the text underneath
  152.        */
  153.       rcol += spaces;
  154.       ccol += spaces;
  155.    }
  156.    check_virtual_col( window, rcol, ccol );
  157. }
  158.  
  159.  
  160. /*
  161.  * Name:    insert_newline
  162.  * Purpose: insert a newline
  163.  * Date:    June 5, 1991
  164.  * Passed:  window:   information allowing access to the current window
  165.  *          carriage_return:   TRUE if carriage return, FALSE if insert line
  166.  *          split_line:   TRUE if split line, FALSE otherwise
  167.  */
  168. void insert_newline( window, carriage_return, split_line )
  169. windows *window;
  170. int carriage_return, split_line;
  171. {
  172. text_ptr source;    /* source for block move to make room for c */
  173. text_ptr dest;      /* destination for block move */
  174. long number;        /* number of characters to be moved */
  175. int len;            /* length of current line */
  176. int pad;            /* padding to add if cursor beyond end of line */
  177. int add;            /* characters to be added (usually 1 in insert mode) */
  178. int i;              /* counter for adding autoindenting */
  179. int rcol;
  180. text_ptr prev;      /* previous lines scanned for autoindent */
  181. int space;
  182. char *trail_space;
  183. file_infos *file;
  184. int prompt_line;
  185.  
  186.    prompt_line = window->bottom_line;
  187.    file = window->file_info;
  188.    window->cursor = cpf( window->cursor );
  189.    copy_line( window->cursor, prompt_line );
  190.    len = linelen( g_status.line_buff );
  191.  
  192.    source = g_status.line_buff + len;
  193.    if (carriage_return || split_line) {
  194.       if (window->rcol < len)
  195.          source = g_status.line_buff + window->rcol;
  196.    }
  197.    dest = source + 1;
  198.    number = linelen( source ) + 2;
  199.    hw_move( dest, source, number );
  200.  
  201.    *source = '\n';
  202.    un_copy_line( window->cursor, prompt_line );
  203.    adjust_start_end( file, 1l );
  204.    adjust_windows_cursor( window, 1l, 1 );
  205.  
  206.    copy_line( window->cursor, prompt_line );
  207.    len = linelen( g_status.line_buff );
  208.    if (len > 0 && g_status.line_buff[len] == '\n') {
  209.       trail_space = g_status.line_buff + len - 1;
  210.       space = 0;
  211.       for (i=len; i>0 && *trail_space == ' '; i--, trail_space--)
  212.          ++space;
  213.       if (space) {
  214.          source = window->cursor + len;
  215.          dest = source - space;
  216.          number = ptoul( g_status.end_mem ) - ptoul( source );
  217.          hw_move( dest, source, number );
  218.          adjust_start_end( file, -space );
  219.          adjust_windows_cursor( window, (long)-space, 0 );
  220.          g_status.end_mem = addltop( (long)-space, g_status.end_mem );
  221.          copy_line( window->cursor, prompt_line );
  222.       }
  223.    }
  224.  
  225.    if (carriage_return || split_line)
  226.       update_line( window );
  227.  
  228.    if  (window->cline == window->bottom_line)
  229.        window_scroll_up( window->top_line, window->bottom_line );
  230.    else
  231.        window_scroll_down( window->cline+1, window->bottom_line );
  232.  
  233.    /*
  234.     * If the cursor is to move down to the next line, then update
  235.     *  the line and column appropriately.
  236.     */
  237.    window->dirty = NOT_LOCAL;
  238.    if (carriage_return || split_line) {
  239.       window->cursor = find_next( window->cursor );
  240.       if (window->cline < window->bottom_line)
  241.          window->cline++;
  242.       window->rline++;
  243.       rcol = window->rcol;
  244.  
  245.       /*
  246.        * indentation is only required if we are in the right mode,
  247.        *  the user typed <CR>, and if there is not space followed
  248.        *  by something after the cursor.
  249.        */
  250.       if (g_status.indent) {
  251.          /*
  252.           * autoindentation is required. Match the indentation of
  253.           *  the first line above that is not blank.
  254.           */
  255.          add = first_non_blank( g_status.line_buff );
  256.          if (g_status.line_buff[add] == '\n' ||
  257.                    g_status.line_buff[add] == CONTROL_Z) {
  258.             prev = cpb( window->cursor );
  259.             while ((prev = find_prev( prev )) != NULL) {
  260.                add = first_non_blank( (char *)prev );
  261.                if (prev[add] != '\n')
  262.                   break;
  263.             }
  264.          }
  265.          copy_line( window->cursor, prompt_line );
  266.          len = linelen( g_status.line_buff );
  267.          source = g_status.line_buff;
  268.          dest = source + add;
  269.          number = len + add + 2;
  270.          hw_move( dest, source, number );
  271.          adjust_start_end( file, add );
  272.  
  273.          /*
  274.           * now put in the autoindent characters
  275.           */
  276.          for (i=add; i>0; i--)
  277.             *source++ = ' ';
  278.  
  279.          window->rcol = add;
  280.          un_copy_line( window->cursor, prompt_line );
  281.          adjust_windows_cursor( window, (long)add, 0 );
  282.          update_line( window );
  283.       } else
  284.          window->rcol = 0;
  285.       if (split_line) {
  286.          window->cursor = cpb( window->cursor );
  287.          window->cursor = find_prev( window->cursor );
  288.          if (window->cline > window->top_line)
  289.             window->cline--;
  290.          window->rline--;
  291.          window->rcol = rcol;
  292.       }
  293.       check_virtual_col( window, window->rcol, window->ccol );
  294.       if (window->dirty)
  295.          window->dirty = GLOBAL;
  296.    }
  297.  
  298.    /*
  299.     * record that file has been modified
  300.     */
  301.    file->modified = TRUE;
  302.    ++file->length;
  303.    restore_marked_block( window, 1 );
  304.    show_size( window );
  305.    show_avail_mem( );
  306. }
  307.  
  308.  
  309. /*
  310.  * Name:    insert_overwrite
  311.  * Purpose: To make the necessary changes after the user has typed a normal
  312.  *           printable character
  313.  * Date:    June 5, 1991
  314.  * Passed:  window:   information allowing access to the current window
  315.  *          c:        the character just typed
  316.  */
  317. void insert_overwrite( window, c )
  318. windows *window;
  319. int c;
  320. {
  321. text_ptr source;    /* source for block move to make room for c */
  322. text_ptr dest;      /* destination for block move */
  323. long number;        /* number of characters to be moved */
  324. int len;            /* length of current line */
  325. int pad;            /* padding to add if cursor beyond end of line */
  326. int i;
  327. int add;            /* characters to be added (usually 1 in insert mode) */
  328. int line;           /* line on screen to save and show prompt */
  329. int space, rcol, ccol;
  330. file_infos *file;
  331.  
  332.    line = window->bottom_line;
  333.    rcol = window->rcol;
  334.    ccol = window->ccol;
  335.    /*
  336.     * first check we have room - the editor can not
  337.     *  cope with lines wider than g_display.line_length
  338.     */
  339.    if (rcol >= g_display.line_length)
  340.       error( WARNING, line, "cannot insert more characters" );
  341.    else {
  342.       file = window->file_info;
  343.       copy_line( window->cursor, line );
  344.  
  345.       /*
  346.        * work out how many characters need to be inserted
  347.        */
  348.       len = linelen( g_status.line_buff );
  349.       if (rcol > len)   /* padding required */
  350.          pad = rcol - len;
  351.       else
  352.          pad = 0;
  353.  
  354.       /*
  355.        * if this is the last line in a file, the last character in the
  356.        * line buffer will be CONTROL_Z.  increment pad and insert a \n.
  357.        */
  358.       if (g_status.line_buff[len] == CONTROL_Z)
  359.          ++pad;
  360.  
  361.       if (g_status.insert || rcol >= len)
  362.          /*
  363.           * inserted characters, or overwritten characters at the end of
  364.           *  the line, are inserted.
  365.           */
  366.          add = 1;
  367.       else
  368.          /*
  369.           *  and no extra space is required to overwrite existing characters
  370.           */
  371.          add = 0;
  372.  
  373.       /*
  374.        * check that current line would not get too long. Note that there must
  375.        *  be space for both the old line and any indentation.
  376.        */
  377.       if (len + pad + add >= g_display.line_length)
  378.          error( WARNING, line, "no more room to add" );
  379.       else {
  380.          /*
  381.           * all clear to add new character!
  382.           */
  383.  
  384.          /*
  385.           * move character to make room for whatever needs to be inserted
  386.           */
  387.          space = 0;
  388.          if (pad > 0  || add > 0) {
  389.             space = pad + add;
  390.             if (g_status.line_buff[len] == CONTROL_Z) {
  391.                if (rcol > len) {
  392.                   g_status.line_buff[rcol+1] = '\n';
  393.                   g_status.line_buff[rcol+2] = CONTROL_Z;
  394.                } else {
  395.                   g_status.line_buff[len] = '\n';
  396.                   g_status.line_buff[len+1] = CONTROL_Z;
  397.                }
  398.                ++file->length;
  399.                show_size( window );
  400.                --pad;
  401.                ++len;
  402.             }
  403.             source = g_status.line_buff + rcol - pad;
  404.             dest = source + pad + add;
  405.             number = len + pad - rcol + 2;
  406.             hw_move( dest, source, number );
  407.             /*
  408.              * if padding was required, then put in the required spaces
  409.              */
  410.             for (i=pad; i>0; i--)
  411.                *source++ = ' ';
  412.          } else
  413.             source = g_status.line_buff + rcol;
  414.          /*
  415.           * now place the new character
  416.           */
  417.          *source = c;
  418.          un_copy_line( window->cursor, line );
  419.  
  420.          /*
  421.           * if we added anything, show the changed line.
  422.           */
  423.          if (pad > 0 || add > 0) {
  424.             update_line( window );
  425.             show_avail_mem( );
  426.          } else
  427.             update_char( window, c, ccol, window->cline );
  428.  
  429.          /*
  430.           * always increment the real column (rcol) and adjust the
  431.           * logical and base column as needed.
  432.           */
  433.          if (ccol < g_display.ncols - 1) {
  434.             ccol++;
  435.             window->dirty = NOT_LOCAL;
  436.             show_changed_line( window );
  437.          } else {
  438.             window->bcol++;
  439.             window->dirty = GLOBAL;
  440.          }
  441.          rcol++;
  442.  
  443.          /*
  444.           * record that file has been modified and adjust cursors and
  445.           * file start and end markers as needed.
  446.           */
  447.          file->modified = TRUE;
  448.          adjust_start_end( file, space );
  449.          adjust_windows_cursor( window, (long)space, 0 );
  450.       }
  451.       window->rcol = rcol;
  452.       window->ccol = ccol;
  453.    }
  454. }
  455.  
  456.  
  457. /*
  458.  * Name:    join_line
  459.  * Purpose: To join current line and line below at cursor
  460.  * Date:    June 5, 1991
  461.  * Passed:  window:   information allowing access to the current window
  462.  * Notes:   trunc the line then join with line below if it exists
  463.  */
  464. void join_line( window )
  465. windows *window;
  466. {
  467. int len;            /* length of current line */
  468. text_ptr source;    /* source for block move to delete word */
  469. text_ptr dest;      /* destination for block move */
  470. long number;        /* number of characters to move */
  471. int del_len;        /* number of characters to delete */
  472. text_ptr p;         /* next line in file */
  473. int pad, i;         /* padding spaces required */
  474. int cr;             /* does current line end with carriage return? */
  475. int line;           /* line on screen to save and show prompt */
  476. file_infos *file;
  477. int rcol;
  478.  
  479.    file = window->file_info;
  480.    line = window->bottom_line;
  481.    rcol = window->rcol;
  482.  
  483.    window->cursor = cpf( window->cursor );
  484.    copy_line( window->cursor, window->bottom_line );
  485.    if (rcol < (len = linelen( g_status.line_buff ))) {
  486.       /*
  487.        * delete rest of line
  488.        */
  489.       dest = g_status.line_buff + rcol;
  490.  
  491.       /*
  492.        * the \n at the end of the line must NOT be deleted
  493.        */
  494.       if (g_status.line_buff[len] == '\n')
  495.          *dest++ = '\n';
  496.  
  497.       len = find_CONTROL_Z( dest );
  498.       adjust_start_end( file, -len );
  499.       *dest = CONTROL_Z;
  500.       un_copy_line( window->cursor, window->bottom_line );
  501.       adjust_windows_cursor( window, (long)-len, 0 );
  502.       window->dirty = GLOBAL;
  503.       file->modified = TRUE;
  504.    }
  505.    /*
  506.     * we need to combine with the next line, if any
  507.     */
  508.    if ((p = find_next( window->cursor )) != NULL) {
  509.       /*
  510.        * add padding if required
  511.        */
  512.       len = linelen( g_status.line_buff );
  513.       if (g_status.line_buff[len] == '\n')
  514.          cr = 1;
  515.       else
  516.          cr = 0;
  517.       if (rcol > len)
  518.          pad = rcol - len;
  519.       else
  520.          pad = 0;
  521.  
  522.       /*
  523.        * check room to combine lines
  524.        */
  525.       if (len + pad + cr + linelen( p ) >= g_display.line_length) {
  526.          error( WARNING, line, "cannot combine lines" );
  527.          return;
  528.       }
  529.  
  530.       /*
  531.        * do the move
  532.        */
  533.       source = g_status.line_buff + rcol - pad;
  534.       dest = source + pad;
  535.       number = len + pad - rcol + 1 + cr;
  536.       hw_move( dest, source, number );
  537.       number = pad - cr;
  538.       adjust_start_end( file, number );
  539.  
  540.       /*
  541.        * insert the padding
  542.        */
  543.       for (i=pad; i>0; i--)
  544.          *source++ = ' ';
  545.  
  546.       /*
  547.        * remove the \n separating the two lines.
  548.        */
  549.       i = 0;
  550.       if (*source == '\n') {
  551.          *source = CONTROL_Z;
  552.          i = -1;
  553.       }
  554.  
  555.       un_copy_line( window->cursor, line );
  556.       adjust_windows_cursor( window, number, i );
  557.       --file->length;
  558.       restore_marked_block( window, -1 );
  559.       show_size( window );
  560.       window->dirty = GLOBAL;
  561.       file->modified = TRUE;
  562.       show_avail_mem( );
  563.    }
  564. }
  565.  
  566.  
  567. /*
  568.  * Name:    dup_line
  569.  * Purpose: Duplicate current line
  570.  * Date:    June 5, 1991
  571.  * Passed:  window:   information allowing access to the current window
  572.  * Notes:   cursor stays on current line
  573.  */
  574. void dup_line( window )
  575. windows *window;
  576. {
  577. int len, i;         /* length of current line */
  578. int line;           /* line on screen to save and show prompt */
  579. long number;
  580. file_infos *file;
  581. text_ptr d, s;
  582.  
  583.    line = window->bottom_line;
  584.    window->cursor = cpf( window->cursor );
  585.  
  586.    /*
  587.     * don't dup the ^Z or a NULL line
  588.     */
  589.    if (*window->cursor != CONTROL_Z && (d=find_next( window->cursor )) !=NULL) {
  590.       file = window->file_info;
  591.  
  592.       /*
  593.        * don't use buffers to dup the line.  use hw_move to make space and
  594.        * copy current line at same time.  d is set to beginning of next line.
  595.        */
  596.       s = window->cursor;
  597.       len = linelen( s );
  598.       if (s[len] == '\n')
  599.         ++len;
  600.       number = ptoul( g_status.end_mem ) - ptoul( s );
  601.       hw_move( d, s, number );
  602.       g_status.end_mem = addltop( len, g_status.end_mem );
  603.       adjust_start_end( file, len );
  604.       adjust_windows_cursor( window, (long)len, 1 );
  605.  
  606.       /*
  607.        * if current line is the bottom line, we can't see the dup line because
  608.        * cursor doesn't move and dup line is added after current line.
  609.        */
  610.       if  (window->cline != line) {
  611.          window_scroll_down( window->cline, line );
  612.          update_line( window );
  613.       }
  614.       window->dirty = NOT_LOCAL;
  615.  
  616.       /*
  617.        * record that file has been modified
  618.        */
  619.       file->modified = TRUE;
  620.       ++file->length;
  621.       restore_marked_block( window, 1 );
  622.       show_size( window );
  623.       show_avail_mem( );
  624.    } else
  625.       error( WARNING, line, "cannot duplicate line" );
  626. }
  627.  
  628.  
  629. /*
  630.  * Name:    char_del_left
  631.  * Purpose: To delete the character to the left of the cursor.
  632.  * Date:    June 5, 1991
  633.  * Passed:  window:   information allowing access to the current window
  634.  * Notes:   If the cursor is at the left of the line, then combine the
  635.  *           current line with the previous one.
  636.  *          If in indent mode, and the cursor is on the first non-blank
  637.  *           character of the line, then match the indentation of an
  638.  *           earlier line.
  639.  */
  640. void char_del_left( window )
  641. windows *window;
  642. {
  643. int len;            /* length of the current line */
  644. text_ptr source;    /* source of block move to delete character */
  645. text_ptr dest;      /* destination of block move */
  646. long number;        /* number of characters to move */
  647. text_ptr p;         /* previous line in file */
  648. int cr;             /* did line end with carriage return? */
  649. int plen;           /* length of previous line */
  650. int del_count;      /* number of characters to delete */
  651. int pos;            /* the position of the first non-blank char */
  652. int prompt_line;    /* line on screen to save and show prompt */
  653. int rcol, ccol, ncols;
  654. file_infos *file;
  655.  
  656.    file = window->file_info;
  657.    prompt_line = window->bottom_line;
  658.    copy_line( window->cursor, prompt_line );
  659.    len = linelen( g_status.line_buff );
  660.    rcol = window->rcol;
  661.    ccol = window->ccol;
  662.    ncols = g_display.ncols;
  663.    if (rcol == 0) {
  664.       /*
  665.        * combine this line with the previous, if any
  666.        */
  667.       window->cursor = cpb( window->cursor );
  668.       if ((p = find_prev( window->cursor )) != NULL) {
  669.          if (g_status.line_buff[len] == '\n')
  670.             cr = 1;
  671.          else
  672.             cr = 0;
  673.          if (len + cr + (plen = linelen( p )) >= g_display.line_length) {
  674.             error( WARNING, prompt_line, "cannot combine lines" );
  675.             return;
  676.          }
  677.  
  678.          /*
  679.           * do the move, use hw_move to do work
  680.           */
  681.          source = window->cursor;
  682.          dest = source - 1;
  683.          number = ptoul( g_status.end_mem ) - ptoul( source );
  684.          hw_move( dest, source, number );
  685.          adjust_start_end( file, -1l );
  686.          g_status.end_mem = cpb( g_status.end_mem );
  687.          g_status.end_mem = g_status.end_mem - 1 ;
  688.  
  689.          /*
  690.           * adjust the cursor line, since it is now in the middle of a
  691.           *  newly formed line
  692.           */
  693.          window->cursor = dest - prelinelen( dest );
  694.  
  695.          /*
  696.           * make sure cursor stays on the screen, at the end of the
  697.           *  previous line
  698.           */
  699.          if (window->cline > window->top_line)
  700.             --window->cline;
  701.          --window->rline;
  702.          rcol = plen;
  703.          ccol = rcol - window->bcol;
  704.          --file->length;
  705.          restore_marked_block( window, -1 );
  706.          adjust_windows_cursor( window, -1l, -1 );
  707.          show_size( window );
  708.          check_virtual_col( window, rcol, ccol );
  709.          window->dirty = GLOBAL;
  710.       }
  711.    } else {
  712.       /*
  713.        * normal delete
  714.        *
  715.        * find out how much to delete (depends on indent mode)
  716.        */
  717.       del_count = 1;   /* the default */
  718.       if (g_status.indent) {
  719.          /*
  720.           * indent only happens if the cursor is on the first
  721.           *  non-blank character of the line
  722.           */
  723.          if ((pos = first_non_blank( g_status.line_buff )) == rcol
  724.                     || g_status.line_buff[pos] == '\n'
  725.                     || g_status.line_buff[pos] == CONTROL_Z) {
  726.             /*
  727.              * now work out how much to indent
  728.              */
  729.             p = cpb( window->cursor );
  730.             for (p=find_prev( p ); p != NULL; p=find_prev( p )) {
  731.                if ((plen = first_non_blank( (char *)p )) < rcol &&
  732.                     *(p+plen) != '\n') {
  733.                   /*
  734.                    * found the line to match
  735.                    */
  736.                   del_count = rcol - plen;
  737.                   break;
  738.                }
  739.             }
  740.          }
  741.       }
  742.  
  743.       /*
  744.        * move text to delete char(s), unless no chars actually there
  745.        */
  746.       if (rcol - del_count < len) {
  747.          dest = g_status.line_buff + rcol - del_count;
  748.          if (rcol > len) {
  749.             source = g_status.line_buff + len;
  750.             number = 2;
  751.          } else {
  752.             source = g_status.line_buff + rcol;
  753.             number = len - rcol + 2;
  754.          }
  755.          hw_move( dest, source, number );
  756.          un_copy_line( window->cursor, prompt_line );
  757.          if (rcol > len)
  758.             number = del_count - (rcol - len);
  759.          else
  760.             number = del_count;
  761.          adjust_start_end( file, -number );
  762.          adjust_windows_cursor( window, -number, 0 );
  763.       }
  764.       rcol -= del_count;
  765.       ccol -= del_count;
  766.       check_virtual_col( window, rcol, ccol );
  767.       if (window->dirty) {
  768.          display_current_window( window );
  769.          window->dirty = NOT_LOCAL;
  770.       } else
  771.          window->dirty = GLOBAL;
  772.       show_changed_line( window );
  773.    }
  774.    file->modified = TRUE;
  775.    show_avail_mem( );
  776. }
  777.  
  778.  
  779. /*
  780.  * Name:    line_kill
  781.  * Purpose: To delete the line the cursor is on.
  782.  * Date:    June 5, 1991
  783.  * Passed:  window:   information allowing access to the current window
  784.  * Notes:   If *window->cursor is pointing to CONTROL_Z then do not do a
  785.  *          line kill (can't kill a NULL line).
  786.  */
  787. void line_kill( window )
  788. windows *window;
  789. {
  790. int i, len;
  791. long number;
  792. text_ptr s, d;         /* next line in file */
  793. windows w;
  794. file_infos *file;
  795.  
  796.    file = window->file_info;
  797.    if (file->length > 0  && *window->cursor != CONTROL_Z) {
  798.  
  799.       /*
  800.        * don't use buffers to delete line.  use hw_move to do work
  801.        */
  802.       s = window->cursor = cpf( window->cursor );
  803.       d = find_next( s );
  804.       len = linelen( s );
  805.       window->dirty = NOT_LOCAL;
  806.  
  807.       /*
  808.        * if line to delete has \n at end of line then decrement file length.
  809.        */
  810.       i = 0;
  811.       if (s[len] == '\n') {
  812.          ++len;
  813.          --file->length;
  814.          --i;
  815.       }
  816.       number = ptoul( g_status.end_mem ) - ptoul( d );
  817.       hw_move( s, d, number );
  818.  
  819.       /*
  820.        * adjust end_mem, all file start and end pointers, and all cursor
  821.        * pointers.
  822.        */
  823.       g_status.end_mem = addltop( -len, g_status.end_mem );
  824.       adjust_start_end( file, -len );
  825.       adjust_windows_cursor( window, -len, i );
  826.       file->modified = TRUE;
  827.       restore_marked_block( window, i );
  828.  
  829.       /*
  830.        * if we are not doing a GLOBAL update then update current window here
  831.        */
  832.       if (window->dirty == NOT_LOCAL) {
  833.          window_scroll_up( window->cline, window->bottom_line );
  834.          dup_window_info( &w, window );
  835.          for (++w.cline,++w.rline; (w.cursor = find_next( w.cursor )) != NULL;
  836.                        w.cline++, w.rline++) {
  837.             if (w.cline == w.bottom_line) {
  838.                update_line( &w );
  839.                break;
  840.             }
  841.          }
  842.       }
  843.  
  844.       /*
  845.        * when doing line delete set columns to 0.
  846.        */
  847.       window->bcol = window->rcol = window->ccol = 0;
  848.       show_size( window );
  849.       show_avail_mem( );
  850.    }
  851. }
  852.  
  853.  
  854. /*
  855.  * Name:    char_del_under
  856.  * Purpose: To delete the character under the cursor.
  857.  * Date:    June 5, 1991
  858.  * Passed:  window:   information allowing access to the current window
  859.  * Notes:   If the cursor is beyond the end of the line, then this
  860.  *           command is ignored.
  861.  */
  862. void char_del_under( window )
  863. windows *window;
  864. {
  865. text_ptr source;    /* source of block move to delete character */
  866. text_ptr dest;      /* destination of block move */
  867. long number;        /* number of characters to move */
  868. int len;
  869. file_infos *file;
  870.  
  871.    copy_line( window->cursor, window->bottom_line );
  872.    if (window->rcol < linelen( g_status.line_buff )) {
  873.       file = window->file_info;
  874.       /*
  875.        * move text to delete char using buffer
  876.        */
  877.       source = g_status.line_buff + window->rcol + 1;
  878.       dest = source - 1;
  879.       len = find_CONTROL_Z( g_status.line_buff );
  880.       number = len - window->rcol;
  881.       hw_move( dest, source, number );
  882.       un_copy_line( window->cursor, window->bottom_line );
  883.  
  884.       /*
  885.        * adjust all pointers
  886.        */
  887.       adjust_start_end( file, -1l );
  888.       adjust_windows_cursor( window, -1l, 0 );
  889.       window->dirty = GLOBAL;
  890.       show_changed_line( window );
  891.       file->modified = TRUE;
  892.       show_avail_mem( );
  893.    }
  894. }
  895.  
  896.  
  897. /*
  898.  * Name:    eol_kill
  899.  * Purpose: To delete everything from the cursor to the end of the line.
  900.  * Date:    June 5, 1991
  901.  * Passed:  window:   information allowing access to the current window
  902.  * Notes:   If the cursor is beyond the end of the line, then this
  903.  *           command is ignored.
  904.  */
  905. void eol_kill( window )
  906. windows *window;
  907. {
  908. char *dest;  /* the start of the delete area */
  909. int len;     /* the length of the current line */
  910. file_infos *file;
  911.  
  912.    copy_line( window->cursor, window->bottom_line );
  913.    if (window->rcol < (len = linelen( g_status.line_buff ))) {
  914.       file = window->file_info;
  915.  
  916.       /*
  917.        * truncate to delete rest of line
  918.        */
  919.       dest = g_status.line_buff + window->rcol;
  920.  
  921.       /*
  922.        * the \n at the end of the line must NOT be deleted!
  923.        */
  924.       if (g_status.line_buff[len] == '\n')
  925.          *dest++ = '\n';
  926.  
  927.       len = find_CONTROL_Z( dest );
  928.       adjust_start_end( file, -len );
  929.       *dest = CONTROL_Z;
  930.       un_copy_line( window->cursor, window->bottom_line );
  931.  
  932.       /*
  933.        * adjust all pointers
  934.        */
  935.       adjust_windows_cursor( window, (long)-len, 0 );
  936.       window->dirty = GLOBAL;
  937.       show_changed_line( window );
  938.       file->modified = TRUE;
  939.       show_avail_mem( );
  940.    }
  941. }
  942.  
  943.  
  944. /*
  945.  * Name:    goto_left
  946.  * Purpose: To move the cursor to the left of the current line.
  947.  * Date:    June 5, 1991
  948.  * Passed:  window:   information allowing access to the current window
  949.  */
  950. void goto_left( window )
  951. windows *window;
  952. {
  953. int rcol;
  954.  
  955.    rcol = first_non_blank( window->cursor );
  956.    if (window->cursor[rcol] == '\n')
  957.       rcol = 0;
  958.    check_virtual_col( window, rcol, window->ccol );
  959. }
  960.  
  961.  
  962. /*
  963.  * Name:    goto_right
  964.  * Purpose: To move the cursor to the right of the current line.
  965.  * Date:    June 5, 1991
  966.  * Passed:  window:   information allowing access to the current window
  967.  */
  968. void goto_right( window )
  969. windows *window;
  970. {
  971.    window->rcol = linelen( window->cursor );
  972.    window->ccol = window->rcol - window->bcol;
  973.    if (window->ccol > g_display.ncols - 1 || window->ccol < 0)
  974.       check_virtual_col( window, window->rcol, window->ccol );
  975. }
  976.  
  977.  
  978. /*
  979.  * Name:    goto_top
  980.  * Purpose: To move the cursor to the top of the current window.
  981.  * Date:    June 5, 1991
  982.  * Passed:  window:   information allowing access to the current window
  983.  * Notes:   If the start of the file occurs before the top of the window,
  984.  *           then the start of the file is moved to the top of the window.
  985.  */
  986. void goto_top( window )
  987. windows *window;
  988. {
  989. text_ptr cursor;  /* anticipated cursor line */
  990.  
  991.    window->cursor = cpb( window->cursor );
  992.    for (; window->cline > window->top_line; window->cline--,window->rline--) {
  993.       if ((cursor = find_prev( window->cursor )) == NULL)
  994.          break;
  995.       window->cursor = cursor;
  996.    }
  997. }
  998.  
  999.  
  1000. /*
  1001.  * Name:    goto_bottom
  1002.  * Purpose: To move the cursor to the bottom of the current window.
  1003.  * Date:    June 5, 1991
  1004.  * Passed:  window:   information allowing access to the current window
  1005.  */
  1006. void goto_bottom( window )
  1007. windows *window;
  1008. {
  1009. text_ptr cursor;
  1010.  
  1011.    window->cursor = cpf( window->cursor );
  1012.    for (; window->cline < window->bottom_line; window->cline++,window->rline++) {
  1013.       if ((cursor = find_next( window->cursor )) == NULL)
  1014.          break;
  1015.       window->cursor = cursor;
  1016.    }
  1017. }
  1018.  
  1019.  
  1020. /*
  1021.  * Name:    set_tabstop
  1022.  * Purpose: To set the current interval between tab stops
  1023.  * Date:    October 1, 1989
  1024.  * Notes:   Tab interval must be reasonable, and this function will
  1025.  *           not allow tabs more than MAX_COLS / 2.
  1026.  */
  1027. void set_tabstop( line )
  1028. int line;
  1029. {
  1030. char num_str[MAX_COLS];  /* tab interval as a character string */
  1031. int tab;                 /* new tab interval */
  1032. int rc;
  1033.  
  1034.    rc = OK;
  1035.    while (rc == OK) {
  1036.       itoa( g_status.tab_size, num_str, 10 );
  1037.       rc = get_name( "Tab interval: ", line, num_str, g_display.message_color );
  1038.       if (rc == OK) {
  1039.          tab = atoi( num_str );
  1040.          if (tab < MAX_COLS/2) {
  1041.             g_status.tab_size = tab;
  1042.             rc = ERROR;
  1043.          } else
  1044.             error( WARNING, line, "tab size too long" );
  1045.       }
  1046.    }
  1047. }
  1048.  
  1049.  
  1050. /*
  1051.  * Name:    show_line_col
  1052.  * Purpose: show current real line and column of current cursor postion
  1053.  * Date:    June 5, 1991
  1054.  * Passed:  window:   information allowing access to the current window
  1055.  * Notes:   Blank old position and display new position.  current line and
  1056.  *          column may take up to 12 columns, which allows the display of
  1057.  *          999 columns and 99,999,999 lines.
  1058.  */
  1059. void show_line_col( windows *window )
  1060. {
  1061. int i, k, pline;
  1062. char line_col[20], num[10];
  1063.  
  1064.    /*
  1065.     * blank out current line:column position.
  1066.     */
  1067.    strcpy( line_col, "            " );
  1068.  
  1069.    /*
  1070.     * convert column to ascii and store in display buffer.
  1071.     */
  1072.    itoa( window->rcol+1, num, 10 );
  1073.    i = strlen( num ) - 1;
  1074.    for (k=11; i>=0; i--, k--)
  1075.       line_col[k] = num[i];
  1076.  
  1077.    /*
  1078.     * put in colon to separate line and column
  1079.     */
  1080.    line_col[k--] = ':';
  1081.  
  1082.    /*
  1083.     * convert line to ascii and store in display buffer.
  1084.     */
  1085.    ltoa( window->rline, num, 10 );
  1086.    i = strlen( num ) - 1;
  1087.    for (; i>=0; i--, k--)
  1088.       line_col[k] = num[i];
  1089.  
  1090.    /*
  1091.     * find line to start line:column display then output
  1092.     */
  1093.    pline = window->top_line - 1;
  1094.    s_output( line_col, pline, MAX_COLS-12, g_display.head_color );
  1095. }
  1096.  
  1097.  
  1098. /*
  1099.  * Name:    command
  1100.  * Purpose: To input and execute a command or printable character.
  1101.  * Date:    June 5, 1991
  1102.  * Passed:  window:   information allowing access to the current window
  1103.  * Notes:   Dispatch editor functions as needed.
  1104.  */
  1105. void command( window, stop )
  1106. windows *window;
  1107. int *stop;
  1108. {
  1109. int c;      /* character entered */
  1110. int temp;   /* temp variable */
  1111. int func;
  1112.  
  1113.    show_line_col( window );
  1114.    c = (c=getch()) != 0 ? c : getch() | 0x100;
  1115.    func = key_func[c].func;
  1116.    switch (func) {
  1117.       case Tab :
  1118.          tab_key( window );
  1119.          break;
  1120.       case BackSpace :
  1121.          char_del_left( window );
  1122.          break;
  1123.       case DeleteChar :
  1124.          char_del_under( window );
  1125.          break;
  1126.       case Rturn :
  1127.          insert_newline( window, TRUE, FALSE );
  1128.          break;
  1129.       case AddLine :
  1130.          insert_newline( window, FALSE, FALSE );
  1131.          break;
  1132.       case SplitLine :
  1133.          insert_newline( window, TRUE, TRUE );
  1134.          break;
  1135.       case JoinLine :
  1136.          join_line( window );
  1137.          break;
  1138.       case DuplicateLine :
  1139.          dup_line( window );
  1140.          break;
  1141.       case ScrollUpLine :
  1142.          scroll_up( window );
  1143.          break;
  1144.       case ScrollDnLine :
  1145.          scroll_down( window );
  1146.          break;
  1147.       case LineDown :
  1148.          move_down( window );
  1149.          break;
  1150.       case LineUp :
  1151.          move_up( window );
  1152.          break;
  1153.       case CharLeft :
  1154.          move_left( window );
  1155.          break;
  1156.       case CharRight :
  1157.          move_right( window );
  1158.          break;
  1159.       case ScreenDown :
  1160.          page_down( window );
  1161.          break;
  1162.       case ScreenUp :
  1163.          page_up( window );
  1164.          break;
  1165.       case WordRight :
  1166.          word_right( window );
  1167.          break;
  1168.       case WordLeft :
  1169.          word_left( window );
  1170.          break;
  1171.       case DeleteLine :
  1172.          line_kill( window );
  1173.          break;
  1174.       case OverWrite :
  1175.          g_status.insert = !g_status.insert;
  1176.          show_modes( );
  1177.          break;
  1178.       case Help :
  1179.          get_help( window );
  1180.          break;
  1181.       case RedrawScreen :
  1182.          force_blank( );
  1183.          break;
  1184.       case Quit :
  1185.          quit( window, stop );
  1186.          break;
  1187.       case Save :
  1188.          save_file( window );
  1189.          break;
  1190.       case SaveAs :
  1191.          save_as_file( window );
  1192.          break;
  1193.       case File :
  1194.          if (window->file_info->modified)
  1195.             save_file( window );
  1196.          finish( window, stop );
  1197.          break;
  1198.       case EditFile :
  1199.          edit_another_file( window );
  1200.          break;
  1201.       case UnMarkBlock :
  1202.          unmark_block( window );
  1203.          break;
  1204.       case MarkBlock :
  1205.          mark_block( window, BLOCK );
  1206.          break;
  1207.       case MarkLine :
  1208.          mark_block( window, LINE );
  1209.          break;
  1210.       case MoveBlock :
  1211.          if (g_status.marked == TRUE)
  1212.             move_copy_delete_overlay_block( window, MOVE );
  1213.          break;
  1214.       case DeleteBlock :
  1215.          if (g_status.marked == TRUE)
  1216.             move_copy_delete_overlay_block( window, DELETE );
  1217.          break;
  1218.       case CopyBlock :
  1219.          if (g_status.marked == TRUE)
  1220.             move_copy_delete_overlay_block( window, COPY );
  1221.          break;
  1222.       case KopyBlock :
  1223.          if (g_status.marked == TRUE)
  1224.             move_copy_delete_overlay_block( window, KOPY );
  1225.          break;
  1226.       case FillBlock :
  1227.          if (g_status.marked == TRUE)
  1228.             move_copy_delete_overlay_block( window, FILL );
  1229.          break;
  1230.       case OverlayBlock :
  1231.          if (g_status.marked == TRUE)
  1232.             move_copy_delete_overlay_block( window, OVERLAY );
  1233.          break;
  1234.       case BlockToFile :
  1235.          block_write( window );
  1236.          break;
  1237.       case PrintBlock :
  1238.          block_print( window );
  1239.          break;
  1240.       case BlockExpandTabs :
  1241.          block_expand_tabs( window );
  1242.          break;
  1243.       case DelEndOfLine :
  1244.          eol_kill( window );
  1245.          break;
  1246.       case FindForward :
  1247.          find_string( window, FORWARD, TRUE );
  1248.          break;
  1249.       case FindBackward :
  1250.          find_string( window, BACKWARD, TRUE );
  1251.          break;
  1252.       case RepeatFindForward :
  1253.          find_string( window, FORWARD, FALSE );
  1254.          break;
  1255.       case RepeatFindBackward :
  1256.          find_string( window, BACKWARD, FALSE );
  1257.          break;
  1258.       case ReplaceForward :
  1259.          replace_string( window, FORWARD );
  1260.          break;
  1261.       case ReplaceBackward :
  1262.          replace_string( window, BACKWARD );
  1263.          break;
  1264.       case BegOfLine :
  1265.          goto_left( window );
  1266.          break;
  1267.       case EndOfLine :
  1268.          goto_right( window );
  1269.          break;
  1270.       case TopOfScreen :
  1271.          goto_top( window );
  1272.          break;
  1273.       case BotOfScreen :
  1274.          goto_bottom( window );
  1275.          break;
  1276.       case TopOfFile :
  1277.          goto_top_file( window );
  1278.          break;
  1279.       case EndOfFile :
  1280.          goto_end_file( window );
  1281.          break;
  1282.       case ParenBalance :
  1283.          match_pair( window );
  1284.          break;
  1285.       case JumpToLine :
  1286.          goto_line( window );
  1287.          break;
  1288.       case Indent :
  1289.          g_status.indent = !g_status.indent;
  1290.          show_indent_mode( );
  1291.          break;
  1292.       case ToggleSearchCase :
  1293.          if (bm.search_case == IGNORE)
  1294.             bm.search_case = MATCH;
  1295.          else
  1296.             bm.search_case = IGNORE;
  1297.          show_search_case( );
  1298.          break;
  1299.       case SetTabs :
  1300.          set_tabstop( window->bottom_line );
  1301.          break;
  1302.       case NextWindow :
  1303.          next_window( window );
  1304.          break;
  1305.       case PreviousWindow :
  1306.          prev_window( window );
  1307.          break;
  1308.       case SplitScreen :
  1309.          split_screen( window );
  1310.          break;
  1311.       case SizeWindow :
  1312.          size_window( window );
  1313.          break;
  1314.       default :
  1315.          if (c >= 0x20 && c < 0x100)
  1316.             insert_overwrite( window, c );
  1317.          break;
  1318.    }
  1319. }
  1320.  
  1321.  
  1322. /*
  1323.  * Name:    editor
  1324.  * Purpose: Set up the editor structures and display changes as needed.
  1325.  * Date:    June 5, 1991
  1326.  * Passed:  argc:   number of command line arguments
  1327.  *          argv:   text of command line arguments
  1328.  */
  1329. void editor( argc, argv )
  1330. int argc;
  1331. char *argv[];
  1332. {
  1333. char *name;  /* name of file to start editing */
  1334. windows *window;            /* current active window */
  1335. windows *above;             /* window above current */
  1336. windows *below;             /* window below current */
  1337. char status_line[MAX_COLS+2]; /* status line at top of window */
  1338. char *p, *q;        /* for setting up status line */
  1339. int count;          /* number of lines updated so far */
  1340. int stop;
  1341.  
  1342.    /*
  1343.     * set up the screen
  1344.     */
  1345.    initialize( );
  1346.  
  1347.    /*
  1348.     * Check that user specified file to edit, if not offer help
  1349.     */
  1350.    if (argc > 1)
  1351.       name = argv[1];
  1352.    else {
  1353.       g_status.rw_name[0] = '\0';
  1354.       name = g_status.rw_name;
  1355.       if (get_name( "File name to edit : ", g_display.nlines, name,
  1356.                     g_display.text_color ) != OK)
  1357.          return;
  1358.       if (*name == '\0')
  1359.          return;
  1360.    }
  1361.  
  1362.    edit_file( name );
  1363.    initialize_window( );
  1364.  
  1365.    window = g_status.current_window;
  1366.    window->dirty = FALSE;
  1367.    show_window_header( window->file_info->file_name, window );
  1368.    show_size_name( window );
  1369.    show_size( window );
  1370.  
  1371.    /*
  1372.     * display the current window
  1373.     */
  1374.    display_current_window( window );
  1375.    show_modes( );
  1376.  
  1377.    /*
  1378.     * main loop - keep updating the display and processing any commands
  1379.     *  while user has not pressed stop
  1380.     */
  1381.    stop = FALSE;
  1382.    for (; stop != TRUE;) {
  1383.       window = g_status.current_window;
  1384.  
  1385.       /*
  1386.        * update all the other windows that point to file that has been changed
  1387.        */
  1388.       above = below = window;
  1389.       while (above->prev || below->next) {
  1390.          if (above->prev) {
  1391.             above = above->prev;
  1392.             if (above->file_info == window->file_info && above->visible)
  1393.                if (window->dirty == GLOBAL || window->dirty == NOT_LOCAL) {
  1394.                   display_current_window( above );
  1395.                   show_size( above );
  1396.                }
  1397.          }
  1398.          if (below->next) {
  1399.             below = below->next;
  1400.             if (below->file_info == window->file_info && below->visible)
  1401.                if (window->dirty == GLOBAL || window->dirty == NOT_LOCAL) {
  1402.                   display_current_window( below );
  1403.                   show_size( below );
  1404.                }
  1405.          }
  1406.       }
  1407.       if (window->dirty == LOCAL || window->dirty == GLOBAL)
  1408.          display_current_window( window );
  1409.       window->dirty = FALSE;
  1410.  
  1411.       /*
  1412.        * all done, so position the cursor and wait for the user to enter
  1413.        *  something
  1414.        */
  1415.       xygoto( window->ccol, window->cline );
  1416.       command( window, &stop );
  1417.    }
  1418.    terminate( );
  1419. }
  1420.